package com.iwdnb.gkgz.common.utils;

import java.math.BigDecimal;
import java.util.Objects;

import lombok.extern.slf4j.Slf4j;

/**
 * @date 2018年7月30日
 * @description 提供精确的浮点数运算(包括加 、 减 、 乘 、 除 、 四舍五入)工具类
 */
@Slf4j
public class BigDecimalUtils {

    // 除法运算默认精度
    private static final int DEF_DIV_SCALE = 2;
    public static final BigDecimal twoHundred = new BigDecimal(200);
    public static final BigDecimal oneHundredTen = new BigDecimal(110);

    public static final BigDecimal oneHundred = new BigDecimal(100);

    public static final BigDecimal ninety = new BigDecimal(90);
    public static final BigDecimal eightyFive = new BigDecimal(85);
    public static final BigDecimal eighty = new BigDecimal(80);
    public static final BigDecimal seventyFive = new BigDecimal(75);
    public static final BigDecimal seventy = new BigDecimal(70);

    public static final BigDecimal fifty = new BigDecimal(50);
    public static final BigDecimal thirty = new BigDecimal(70);
    public static final BigDecimal twentyFive = new BigDecimal(25);
    public static final BigDecimal twenty = new BigDecimal(20);
    public static final BigDecimal fifteen = new BigDecimal(15);
    public static final BigDecimal ten = new BigDecimal(10);
    public static final BigDecimal ninePointSix = new BigDecimal(9.6);
    public static final BigDecimal nine = new BigDecimal(9);
    public static final BigDecimal eightPointEight = new BigDecimal(8.8);
    public static final BigDecimal eight = new BigDecimal(8);
    public static final BigDecimal seven = new BigDecimal(7);
    public static final BigDecimal sixPointEight = new BigDecimal(6.8);
    public static final BigDecimal sixPointFive = new BigDecimal(6.5);
    public static final BigDecimal six = new BigDecimal(6);
    public static final BigDecimal five = new BigDecimal(5);
    public static final BigDecimal four = new BigDecimal(4);
    public static final BigDecimal threePointFive = new BigDecimal(3.5);
    public static final BigDecimal three = new BigDecimal(3);
    public static final BigDecimal twoPointNine = new BigDecimal(2.9);
    public static final BigDecimal twoPointEight = new BigDecimal(2.8);
    public static final BigDecimal twoPointSeven = new BigDecimal(2.7);
    public static final BigDecimal twoPointSix = new BigDecimal(2.6);
    public static final BigDecimal twoPointFive = new BigDecimal(2.5);
    public static final BigDecimal twoPointFour = new BigDecimal(2.4);
    public static final BigDecimal twoPointThree = new BigDecimal(2.3);
    public static final BigDecimal twoPointTwoFive = new BigDecimal(2.25);
    public static final BigDecimal twoPointTwo = new BigDecimal(2.2);
    public static final BigDecimal twoPointOne = new BigDecimal(2.1);
    public static final BigDecimal two = new BigDecimal(2);
    public static final BigDecimal onePointNine = new BigDecimal(1.9);
    public static final BigDecimal onePointEight = new BigDecimal(1.8);
    public static final BigDecimal onePointSeven = new BigDecimal(1.7);
    public static final BigDecimal onePointSix = new BigDecimal(1.6);

    public static final BigDecimal onePointFive = new BigDecimal(1.5);
    public static final BigDecimal onePointTwoFive = new BigDecimal("1.25");
    public static final BigDecimal onePointZeroOneTwo = new BigDecimal("1.012");
    public static final BigDecimal onePointZeroOneOne = new BigDecimal(1.011);
    public static final BigDecimal onePointZeroOne = new BigDecimal(1.01);

    public static final BigDecimal onePointZeroZeroTwo = new BigDecimal(1.002);

    public static final BigDecimal onePointOne = new BigDecimal(1.1);
    public static final BigDecimal one = new BigDecimal(1);
    public static final BigDecimal zeroPointNineNineFive = new BigDecimal(0.995);
    public static final BigDecimal zeroPointNineEightEight = new BigDecimal(0.988);

    public static final BigDecimal zeroPointNineEight = new BigDecimal(0.98);

    public static final BigDecimal zeroPointNineSevenFive = new BigDecimal(0.975);

    public static final BigDecimal zeroPointNineSeven = new BigDecimal(0.97);
    public static final BigDecimal zeroPointNineSixFive = new BigDecimal(0.965);

    public static final BigDecimal zeroPointNineSix = new BigDecimal(0.96);
    public static final BigDecimal zeroPointNineFiveThree = new BigDecimal(0.953);

    public static final BigDecimal zeroPointNineFive = new BigDecimal(0.95);

    public static final BigDecimal zeroPointNine = new BigDecimal(0.9);
    public static final BigDecimal zeroPointEight = new BigDecimal(0.8);
    public static final BigDecimal zeroPointSix = new BigDecimal(0.6);
    public static final BigDecimal zeroPointFive = new BigDecimal(0.5);

    public static final BigDecimal zeroPointThree = new BigDecimal(0.3);

    public static final BigDecimal zeroPointZeroOne = new BigDecimal(0.01);
    public static final BigDecimal zeroPointZeroZeroFive = new BigDecimal(0.005);

    public static final BigDecimal zero = new BigDecimal(0);
    public static final BigDecimal fuZeroPointZeroZeroZeroZeroOne = new BigDecimal(-0.00001);


    public static final BigDecimal fuZeroPointFour = new BigDecimal(-0.4);
    public static final BigDecimal fuZeroPointFive = new BigDecimal(-0.5);
    public static final BigDecimal fuOne = new BigDecimal(-1);
    public static final BigDecimal fuOnePointOne = new BigDecimal(-1.1);
    public static final BigDecimal fuOnePointFive = new BigDecimal(-1.5);
    public static final BigDecimal fuTwo = new BigDecimal(-2);
    public static final BigDecimal fuThree = new BigDecimal(-3);
    public static final BigDecimal fuThreePointFive = new BigDecimal(-3.5);

    public static final BigDecimal fuFour = new BigDecimal(-4);

    public static final BigDecimal fuFourPointEight = new BigDecimal(-4.8);
    public static final BigDecimal fuFive = new BigDecimal(-5);
    public static final BigDecimal fuSix = new BigDecimal(-6);
    public static final BigDecimal fuSeven = new BigDecimal(-7);
    public static final BigDecimal fuEight = new BigDecimal(-8);
    public static final BigDecimal fuNine = new BigDecimal(-9);
    public static final BigDecimal fuTen = new BigDecimal(-10);
    public static final BigDecimal fuTwenty = new BigDecimal(-20);
    public static final BigDecimal fuFourty = new BigDecimal(-40);
    public static final BigDecimal fuOneHundred = new BigDecimal(-100);
    public static final BigDecimal fuOneHundredTen = new BigDecimal(-110);


    private BigDecimalUtils() {

    }

    public static BigDecimal of(Double o) {
        return of(o, 2);
    }

    public static BigDecimal of(Double o, int scale) {
        return new BigDecimal(o).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    public static BigDecimal of(String o) {
        return of(o, 2);
    }

    public static BigDecimal of(Integer o) {
        return of(o.toString(), 0);
    }

    public static BigDecimal of(String o, int scale) {
        return new BigDecimal(o).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    public static BigDecimal round(BigDecimal v) {
        return v.setScale(2, BigDecimal.ROUND_HALF_UP);
    }

    public static BigDecimal round(BigDecimal v, int scale) {
        return v.setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    public static String toString(BigDecimal v, int scale) {
        return round(v, scale).toString();
    }

    /**
     * 精确加法
     */
    public static BigDecimal add(String value1, String value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        BigDecimal add = b1.add(b2);
        BigDecimal bigDecimal = add.setScale(2, BigDecimal.ROUND_HALF_UP);
        return bigDecimal;
    }

    /**
     * 精确加法
     */
    public static BigDecimal add(String value1, String value2, String value3) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        BigDecimal b3 = new BigDecimal(value3);
        BigDecimal add = b1.add(b2).add(b3);
        BigDecimal bigDecimal = add.setScale(2, BigDecimal.ROUND_HALF_UP);
        return bigDecimal;
    }

    /**
     * 精确加法
     */
    public static BigDecimal add(BigDecimal value1, BigDecimal value2) {
        BigDecimal add = value1.add(value2);
        BigDecimal bigDecimal = add.setScale(2, BigDecimal.ROUND_HALF_UP);
        return bigDecimal;
    }

    /**
     * 精确加法
     */
    public static BigDecimal add(BigDecimal value1, BigDecimal value2, BigDecimal value3) {
        BigDecimal add = value1.add(value2).add(value3);
        BigDecimal bigDecimal = add.setScale(2, BigDecimal.ROUND_HALF_UP);
        return bigDecimal;
    }

    /**
     * 精确减法
     */
    public static BigDecimal subtract(String value1, String value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        BigDecimal subtract = b1.subtract(b2);
        BigDecimal bigDecimal = subtract.setScale(2, BigDecimal.ROUND_HALF_UP);
        return bigDecimal;
    }

    /**
     * 精确减法
     */
    public static BigDecimal subtract(String value1, String value2, String value3) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        BigDecimal b3 = new BigDecimal(value3);
        BigDecimal subtract = b1.subtract(b2).subtract(b3);
        BigDecimal bigDecimal = subtract.setScale(2, BigDecimal.ROUND_HALF_UP);
        return bigDecimal;
    }

    /**
     * 精确减法
     */
    public static BigDecimal subtract(BigDecimal value1, BigDecimal value2) {
        return subtract(value1,value2,2);
    }

    /**
     * 精确减法
     */
    public static BigDecimal subtract(BigDecimal value1, BigDecimal value2,int scale) {
        BigDecimal subtract = value1.subtract(value2);
        BigDecimal bigDecimal = subtract.setScale(scale, BigDecimal.ROUND_HALF_UP);
        return bigDecimal;
    }
    /**
     * 精确减法
     */
    public static BigDecimal subtract(BigDecimal value1, BigDecimal value2, BigDecimal value3) {
        BigDecimal subtract = value1.subtract(value2).subtract(value3);
        BigDecimal bigDecimal = subtract.setScale(2, BigDecimal.ROUND_HALF_UP);
        return bigDecimal;
    }

    /**
     * 精确乘法
     */
    public static BigDecimal multiply(String value1, String value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        BigDecimal multiply = b1.multiply(b2);
        BigDecimal aamont = multiply.setScale(2, BigDecimal.ROUND_HALF_UP);
        return aamont;
    }

    /**
     * 精确乘法
     */
    public static BigDecimal multiply(String value1, String value2, String value3) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        BigDecimal b3 = new BigDecimal(value3);
        BigDecimal multiply = b1.multiply(b2).multiply(b3);
        BigDecimal aamont = multiply.setScale(2, BigDecimal.ROUND_HALF_UP);
        return aamont;
    }

    /**
     * 精确乘法
     */
    public static BigDecimal multiply(BigDecimal value1, BigDecimal value2) {
        return multiply(value1, value2, 2);
    }

    /**
     * 精确乘法
     */
    public static BigDecimal multiply(BigDecimal value1, BigDecimal value2, int scale) {
        BigDecimal multiply = value1.multiply(value2);
        BigDecimal aamont = multiply.setScale(scale, BigDecimal.ROUND_HALF_UP);
        return aamont;
    }

    /**
     * 精确乘法
     */
    public static BigDecimal multiply(BigDecimal value1, BigDecimal value2, BigDecimal value3) {
        BigDecimal multiply = value1.multiply(value2).multiply(value3);
        BigDecimal aamont = multiply.setScale(2, BigDecimal.ROUND_HALF_UP);
        return aamont;
    }

    /**
     * 精确除法
     *
     * @param
     */
    public static BigDecimal divide(String value1, String value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        BigDecimal bigDecimal = b1.divide(b2, 2, BigDecimal.ROUND_DOWN).setScale(2, BigDecimal.ROUND_HALF_UP);
        return bigDecimal;
    }

    /**
     * 精确除法
     *
     * @param
     */
    public static BigDecimal divide(BigDecimal value1, BigDecimal value2) {
        return divide(value1, value2, 2);
    }

    /**
     * 精确除法
     *
     * @param
     */
    public static BigDecimal divide(BigDecimal value1, BigDecimal value2, int scale) {
        BigDecimal bigDecimal = value1.divide(value2, scale, BigDecimal.ROUND_HALF_UP).setScale(scale,
            BigDecimal.ROUND_HALF_UP);
        return bigDecimal;
    }

    /**
     * 计算差额百分比
     *
     * @param
     */
    public static BigDecimal subStractAndDividePrecent(BigDecimal value1, BigDecimal value2) {
        BigDecimal range = value1.subtract(value2);
        return divideToPrecent(range, value2);
    }

    /**
     * 精确除法
     *
     * @param
     */
    public static BigDecimal divideToPrecent(BigDecimal value1, BigDecimal value2) {
        if (value2.equals(BigDecimal.ZERO) || value2.equals(new BigDecimal("0.00"))) {
            value2 = new BigDecimal("0.01");
        }
        try {
            return value1.multiply(new BigDecimal("100")).divide(value2, 2, BigDecimal.ROUND_DOWN).setScale(2,
                BigDecimal.ROUND_HALF_UP);
        } catch (Exception e) {
            log.info("v1:{},v2:{}", value1, value2);
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 入库
     *
     * 加权平均单价=（个数*价钱+本期个数*价钱）/（期初数量+本期购进数量）
     * 加权平均价 进货
     * primeNumbers  期初个数
     * primePrice   期初价钱
     *
     * currentNumbers 本期数量
     * currentPrice   本期价钱
     *
     * @param
     */
    public static BigDecimal wapAdvance(BigDecimal primeNumbers, BigDecimal primePrice, BigDecimal currentNumbers,
        BigDecimal currentPrice) {

        BigDecimal prime = primeNumbers.multiply(primePrice);
        BigDecimal current = currentNumbers.multiply(currentPrice);
        BigDecimal primeCurrent = prime.add(current);
        BigDecimal num = primeNumbers.add(currentNumbers);
        BigDecimal bigDecimal = primeCurrent.divide(num, 2, BigDecimal.ROUND_DOWN).setScale(2,
            BigDecimal.ROUND_HALF_UP);
        return bigDecimal;
    }

    /**
     * 退货
     *
     * 出价
     * （加权值 * 期初数量 -本期退货成本*本期退货数量）/  （库存-本期退货数量）
     *
     * 加权平均价 出货
     * primeNumbers  期初个数
     * primePrice   期初价钱
     *
     * currentNumbers 本期退货数量
     * currentPrice   本期退货价钱
     *
     * @param
     */
    public static BigDecimal wapOut(BigDecimal primeNumbers, BigDecimal primePrice, BigDecimal currentNumbers,
        BigDecimal currentPrice) {
        BigDecimal primeMultiply = primePrice.multiply(primeNumbers);

        BigDecimal currentUltiply = currentPrice.multiply(currentNumbers);
        BigDecimal primeCurrentSubtract = primeMultiply.subtract(currentUltiply);

        BigDecimal divideSubtract = primeNumbers.subtract(currentNumbers);

        BigDecimal bigDecimal = primeCurrentSubtract.divide(divideSubtract, 3, BigDecimal.ROUND_DOWN).setScale(3,
            BigDecimal.ROUND_HALF_UP);
        return bigDecimal;
    }

    public static Boolean isGt(BigDecimal a, BigDecimal b) {
        if (Objects.isNull(a)) {
            return false;
        }
        if (Objects.isNull(b)) {
            return true;
        }
        return a.compareTo(b) > 0;
    }

    public static Boolean isGe(BigDecimal a, BigDecimal b) {
        if (Objects.isNull(a)) {
            return false;
        }
        if (Objects.isNull(b)) {
            return true;
        }
        return a.compareTo(b) >= 0;
    }

    public static Boolean isLt(BigDecimal a, BigDecimal b) {
        if (Objects.isNull(a)) {
            return true;
        }
        if (Objects.isNull(b)) {
            return false;
        }
        return a.compareTo(b) < 0;
    }

    public static Boolean isLe(BigDecimal a, BigDecimal b) {
        if (Objects.isNull(a)) {
            return true;
        }
        if (Objects.isNull(b)) {
            return false;
        }
        return a.compareTo(b) <= 0;
    }

    public static Boolean eq(BigDecimal a, BigDecimal b) {
        if (Objects.isNull(a) && Objects.isNull(b)) {
            return true;
        }
        return a.compareTo(b) == 0;
    }

    /**
     * 价格是否粘合
     *
     * @param a
     * @param b
     * @return
     */
    public static Boolean isApproximate(BigDecimal a, BigDecimal b) {
        return isApproximate(a, b, zeroPointFive);
    }

    public static Boolean isApproximate(BigDecimal a, BigDecimal b, BigDecimal compareRate) {
        if (Objects.isNull(a) || Objects.isNull(b)) {
            return false;
        }
        BigDecimal rate = subStractAndDividePrecent(a, b).abs();
        return BigDecimalUtils.isLe(rate, compareRate);
    }

}


